#ifndef OAHO_PIPE_H
#define OAHO_PIPE_H
#ifdef __linux__
#ifndef _GNU_SOURCE
#define _GNU_SOURCE
#endif
#include <unistd.h>
#include <fcntl.h>
#include <string.h>
#include <cstdio>
#include <errno.h>
#include <exception>
extern int errno;
/**
 * create a pipe, a undirectional data channel that can be 
 * used for interprocess communication. the array pipefd is used
 * to return two file descriptors referring to the ends of the pipe.
 * pipe[0] refers to the read  end of the pipe
 * pipe[1] refers to the write end of the pipe
 **/
class unidirection_pipe{
public:
    unidirection_pipe(int flags = O_NONBLOCK);
    ~unidirection_pipe();
    inline const int read_fd();
    inline const int write_fd();
    inline ssize_t   write(const void* buf, size_t n);
    inline ssize_t   read(void* buf, size_t n);
private:
    inline void handle_error(int);
private:   
    int pair[2];
};

unidirection_pipe::unidirection_pipe(int flags){
    /*https://man7.org/linux/man-pages/man2/pipe.2.html*/
    int ret = pipe2(pair, flags);
    if( ret < 0){
        int err_code = errno;
        handle_error(err_code);
    }
}
unidirection_pipe::~unidirection_pipe(){
    ::close(pair[0]);
    ::close(pair[1]);
}

inline const int unidirection_pipe::read_fd(){
    return pair[0];
}
inline const int unidirection_pipe::write_fd(){
    return pair[1];
}
inline ssize_t  unidirection_pipe::write(const void* buf, size_t n){
    int ret = ::write(pair[1], buf, n);
    if( ret < 0){
        int err_code = errno;
        handle_error(err_code);
    }
    return ret;
}
inline ssize_t  unidirection_pipe::read(void* buf, size_t n){
    int ret = ::read(pair[0], buf, n);
    if( ret < 0){
        int err_code = errno;
        handle_error(err_code);
    }
    return ret;
}
inline void unidirection_pipe::handle_error(int code){
    throw std::runtime_error(strerror(code));
}
#endif
#endif